package com.xyht.medicine_management_system.modules.payment.wxpay.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wechat.pay.java.core.notification.RequestParam;
import com.xyht.medicine_management_system.common.commonReturn.CommonCodeEnum;
import com.xyht.medicine_management_system.common.commonReturn.CommonResult;
import com.xyht.medicine_management_system.common.commonReturn.ExceptionCast;
import com.xyht.medicine_management_system.common.util.TimeUtil;
import com.xyht.medicine_management_system.modules.coupon.constant.UserCouponRelationConstant;
import com.xyht.medicine_management_system.modules.coupon.entity.MmsUserCouponRelation;
import com.xyht.medicine_management_system.modules.coupon.entity.resp.MmsUserCouponRelationResp;
import com.xyht.medicine_management_system.modules.coupon.service.MmsUserCouponRelationService;
import com.xyht.medicine_management_system.modules.order.constant.*;
import com.xyht.medicine_management_system.modules.order.entity.MmsOrder;
import com.xyht.medicine_management_system.modules.order.entity.MmsOrderMedicineInfo;
import com.xyht.medicine_management_system.modules.order.entity.MmsOrderRefund;
import com.xyht.medicine_management_system.modules.order.entity.MmsOrderSchedule;
import com.xyht.medicine_management_system.modules.order.entity.resp.OrderMedicineInfoResp;
import com.xyht.medicine_management_system.modules.order.mapper.MmsOrderMapper;
import com.xyht.medicine_management_system.modules.order.mapper.MmsOrderMedicineInfoMapper;
import com.xyht.medicine_management_system.modules.order.mapper.MmsOrderRefundMapper;
import com.xyht.medicine_management_system.modules.order.mapper.MmsOrderScheduleMapper;
import com.xyht.medicine_management_system.modules.order.util.OrderScheduleUtil;
import com.xyht.medicine_management_system.modules.order.util.OrderUtil;
import com.xyht.medicine_management_system.modules.payment.wxpay.constant.OriginalTypeConstant;
import com.xyht.medicine_management_system.modules.payment.wxpay.entity.CallbackBodyEntity;
import com.xyht.medicine_management_system.modules.payment.wxpay.entity.CallbackBodyResourceEntity;
import com.xyht.medicine_management_system.modules.payment.wxpay.entity.CallbackPayResourceEntity;
import com.xyht.medicine_management_system.modules.payment.wxpay.service.WxService;
import com.xyht.medicine_management_system.modules.payment.wxpay.util.WxPayUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import static com.xyht.medicine_management_system.modules.payment.constant.CommonConstant.SIGN_TYPE;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.xyht.medicine_management_system.common.util.ParamCheckUtil.isNullOrEmpty;

@Service
public class WxServiceImpl implements WxService {
    @Resource
    private PlatformTransactionManager transactionManager; // 事务
    @Resource
    private WxPayUtil wxPayUtil;
    @Resource
    private MmsOrderMapper mmsOrderMapper;
    @Resource
    private MmsOrderScheduleMapper mmsOrderScheduleMapper;
    @Resource
    private OrderUtil orderUtil;
    @Resource
    private OrderScheduleUtil orderScheduleUtil;
    @Resource
    private MmsOrderRefundMapper mmsOrderRefundMapper;
    @Resource
    private MmsUserCouponRelationService mmsUserCouponRelationService;
    @Resource
    private MmsOrderMedicineInfoMapper mmsOrderMedicineInfoMapper;

    @Override
    public String wxPayCallback(CallbackBodyEntity entity, HttpServletRequest request, HttpServletResponse response) {
        CallbackBodyResourceEntity body = new CallbackBodyResourceEntity();
        BeanUtils.copyProperties(entity, body);
        RequestParam requestParam = new RequestParam.Builder()
                .signType(SIGN_TYPE)
                .serialNumber(request.getParameter("Wechatpay-Serial"))
                .signature(request.getParameter("Wechatpay-Signature"))
                .timestamp(request.getParameter("Wechatpay-Timestamp"))
                .nonce(request.getParameter("Wechatpay-Nonce"))
                .body(JSONObject.toJSONString(body))
                .build();
        CallbackPayResourceEntity resourceEntity = wxPayUtil.checkNotificationSign(requestParam);
        if (resourceEntity == null) {
            return null;
        }

        // 开启事务
        DefaultTransactionDefinition dt = new DefaultTransactionDefinition();
        // 嵌套事务 PROPAGATION_REQUIRES_NEW 每次开启一个新的事务
        dt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        // 设置嵌套事务
        TransactionStatus status = transactionManager.getTransaction(dt);
        try {
            switch (entity.getResource().getOriginal_type()) {
                case OriginalTypeConstant.ORIGINAL_TYPE_TRANSACTION: // 支付结果通知
                    // 交易状态不为 SUCCESS：支付成功,则不进行操作
                    if (!resourceEntity.getTrade_state().equals("SUCCESS")) {
                        break;
                    }
                    // 查询订单是否存在
                    MmsOrder order = mmsOrderMapper.selectById(resourceEntity.getOut_trade_no());
                    // 订单不存在
                    if (order == null) {
                        break;
                    }
                    // 订单已支付
                    if (orderUtil.isPaid(order.getId())) {
                        break;
                    }

                    // 关闭定时任务
                    List<MmsOrderSchedule> paymentScheduleList = mmsOrderScheduleMapper.selectList(new LambdaQueryWrapper<MmsOrderSchedule>()
                            .eq(MmsOrderSchedule::getOrderId, order.getId())
                            .eq(MmsOrderSchedule::getIsClose, false) // 状态未结束
                            .eq(MmsOrderSchedule::getType, OrderScheduleConstant.ORDER_SCHEDULE_TYPE_PAYMENT)); // 支付进度
                    if (!paymentScheduleList.isEmpty()) {
                        // 关闭支付定时任务
                        for (MmsOrderSchedule paymentSchedule : paymentScheduleList) {
                            paymentSchedule.setIsClose(true); // 定时任务结束
                            paymentSchedule.setEndTime(new Date()); // 结束时间
                            paymentSchedule.setUpdateUserId(ProcessApproverConstant.PROCESS_APPROVER_SYSTEM); // 修改人
                            int updNum = mmsOrderScheduleMapper.updateById(paymentSchedule);
                            if (updNum != 1) { // 修改失败
                                rollback(status); // 手动回滚事务
                                return failResp();
                            }
                        }
                    }

                    // 记录订单包含药品信息
                    List<OrderMedicineInfoResp> medicineInfoRespList = JSON.parseArray(order.getMedicineInfo(), OrderMedicineInfoResp.class);
                    for (OrderMedicineInfoResp medicineInfoResp : medicineInfoRespList) {
                        MmsOrderMedicineInfo orderMedicineInfo = new MmsOrderMedicineInfo();
                        orderMedicineInfo.setOrderId(order.getId());
                        orderMedicineInfo.setType(OrderMedicineInfoConstant.ORDER_MEDICINE_INFO_ORDER);
                        orderMedicineInfo.setMedicineInfo(JSON.toJSONString(medicineInfoResp.getMedicine()));
                        orderMedicineInfo.setMedicineCode(medicineInfoResp.getMedicine().getMedicineCode());
                        orderMedicineInfo.setNum(medicineInfoResp.getMedicineNum());
                        int medicineInfoInsertNum = mmsOrderMedicineInfoMapper.insert(orderMedicineInfo);
                        if (medicineInfoInsertNum != 1) {
                            ExceptionCast.cast(CommonResult.failed(CommonCodeEnum.FAIL));
                        }
                    }

                    // 修改订单状态等信息
                    order.setPaymentTransactionId(resourceEntity.getTransaction_id()); // 支付订单号
                    order.setPaymentAmount(centsToYuan(resourceEntity.getAmount().getTotal())); // 支付金额
                    order.setPaymentMethod(OrderConstant.ORDER_PAYMENT_METHOD_WXPAY); // 订单支付方式: 微信支付
                    order.setPaymentTime(TimeUtil.rfc3339ParseDate(resourceEntity.getSuccess_time())); // 支付时间
                    order.setOrderStatus(OrderConstant.ORDER_STATUS_TO_BE_DELIVERED); // 待发货
                    int orderUpdNum = mmsOrderMapper.updateById(order);
                    if (orderUpdNum != 1) {
                        rollback(status); // 手动回滚事务
                        return failResp();
                    }

                    // 创建订单定时任务(发货进度)
                    MmsOrderSchedule deliverySchedule = orderScheduleUtil.generateOrderScheduleByType(
                            order.getId(),
                            OrderScheduleConstant.ORDER_SCHEDULE_TYPE_DELIVERY,
                            OrderScheduleConstant.ORDER_SCHEDULE_CREATOR_SYSTEM);
                    if (deliverySchedule == null) {
                        rollback(status); // 手动回滚事务
                        return failResp();
                    }
                    // 创建订单定时任务(订单结束进度)
                    MmsOrderSchedule orderEndSchedule = orderScheduleUtil.generateOrderScheduleByType(
                            order.getId(),
                            OrderScheduleConstant.ORDER_SCHEDULE_TYPE_ORDER_END,
                            OrderScheduleConstant.ORDER_SCHEDULE_CREATOR_SYSTEM);
                    if (orderEndSchedule == null) {
                        rollback(status); // 手动回滚事务
                        return failResp();
                    }

                    break;
                case OriginalTypeConstant.ORIGINAL_TYPE_REFUND: // 退款结果通知
                    // 交易状态不为 SUCCESS：支付成功,则不进行操作
                    if (!resourceEntity.getRefund_status().equals("SUCCESS")) {
                        break;
                    }
                    // 查询退款记录是否存在
                    MmsOrderRefund orderRefund = mmsOrderRefundMapper.selectById(resourceEntity.getOut_refund_no());
                    if (orderRefund == null) {
                        break;
                    }
                    // 已退款
                    if (orderRefund.getRefundAmount() != null && orderRefund.getRefundAmount().compareTo(BigDecimal.valueOf(0)) > 0) {
                        break;
                    }
                    // 查询订单是否存在
                    MmsOrder refundOrder = mmsOrderMapper.selectById(resourceEntity.getOut_trade_no());
                    // 订单不存在
                    if (refundOrder == null) {
                        break;
                    }

                    // 退还优惠券
                    if (!isNullOrEmpty(refundOrder.getCouponInfo())) { // 存在优惠券信息
                        MmsUserCouponRelationResp couponInfo = JSONObject.parseObject(refundOrder.getCouponInfo(), MmsUserCouponRelationResp.class);
                        if (couponInfo != null) {
                            MmsUserCouponRelation userCoupon = mmsUserCouponRelationService.getById(couponInfo.getId());
                            if (userCoupon != null) {
                                // 优惠券状态修改为未使用
                                userCoupon.setIsUse(UserCouponRelationConstant.USER_COUPON_RELATION_NOT_USE);
                                boolean couponUpdSuccess = mmsUserCouponRelationService.updateById(userCoupon);
                                if (!couponUpdSuccess) { // 修改失败
                                    rollback(status); // 手动回滚事务
                                    return failResp();
                                }
                            }
                        }
                    }

                    // 修改退款记录
                    orderRefund.setRefundId(resourceEntity.getRefund_id()); // 微信退款id
                    orderRefund.setRefundAmount(centsToYuan(resourceEntity.getAmount().getTotal())); // 退款金额
                    orderRefund.setRefundTime(TimeUtil.rfc3339ParseDate(resourceEntity.getSuccess_time())); // 退款时间
                    orderRefund.setStatus(OrderRefundConstant.ORDER_REFUND_STATUS_REFUNDED); // 退货状态: 已退款
                    int refundUpdNum = mmsOrderRefundMapper.updateById(orderRefund);
                    if (refundUpdNum != 1) {
                        rollback(status); // 手动回滚事务
                        return failResp();
                    }

                    // 修改订单包含药品信息
                    MmsOrderMedicineInfo orderMedicineInfo = new MmsOrderMedicineInfo();
                    orderMedicineInfo.setRefundId(orderRefund.getId());
                    orderMedicineInfo.setType(OrderMedicineInfoConstant.ORDER_MEDICINE_INFO_REFUND);
                    int orderMedicineInfoUpdNum = mmsOrderMedicineInfoMapper.update(orderMedicineInfo, new LambdaQueryWrapper<MmsOrderMedicineInfo>()
                            .eq(MmsOrderMedicineInfo::getOrderId, refundOrder.getId()));
                    if (orderMedicineInfoUpdNum < 1) {
                        ExceptionCast.cast(CommonResult.failed(CommonCodeEnum.FAIL));
                    }

                    // 修改订单状态
                    Date nowDate = new Date(); // 当前时间
                    refundOrder.setIsClose(OrderConstant.ORDER_IS_CLOSE_YES); // 订单结束
                    refundOrder.setEndTime(nowDate); // 订单结束时间
                    int refundOrderUpdNum = mmsOrderMapper.updateById(refundOrder);
                    if (refundOrderUpdNum != 1) { // 修改失败
                        rollback(status); // 手动回滚事务
                        return failResp();
                    }

                    break;
                case OriginalTypeConstant.ORIGINAL_TYPE_PROFIT_SHARING: // 分账动账通知
                    MmsOrder profitOrder = mmsOrderMapper.selectById(resourceEntity.getOut_order_no());
                    if (profitOrder == null) {
                        break;
                    }

                    profitOrder.setProfitSharingTime(TimeUtil.rfc3339ParseDate(resourceEntity.getSuccess_time()));
                    int profitOrderUpdNum = mmsOrderMapper.updateById(profitOrder);
                    if (profitOrderUpdNum != 1) {
                        rollback(status); // 手动回滚事务
                        return failResp();
                    }

                    break;
            }

        } catch (Exception e) {
            e.printStackTrace();
            // 手动回滚事务
            transactionManager.rollback(status);
        } finally {
            if (status.isNewTransaction() && !status.isCompleted()) {
                transactionManager.commit(status);
            }
        }
        return null;
    }

    /**
     * 将以分为单位的金额(Integer)转换为以元为单位的金额(BigDecimal)
     *
     * @param moneyInCents 以分为单位的金额(Integer)
     * @return 以元为单位的金额(BigDecimal)
     */
    private BigDecimal centsToYuan(Integer moneyInCents) {
        // 将分转换为元, 需要除以100, 并将结果存储为BigDecimal类型
        return new BigDecimal(moneyInCents).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
    }

    /**
     * 失败应答
     */
    private String failResp() {
        Map<String, String> map = new HashMap<>();
        map.put("code", "FAIL");
        map.put("message", "失败");
        return JSON.toJSONString(map);
    }

    /**
     * 手动回滚事务
     *
     * @param status 嵌套事务
     */
    private void rollback(TransactionStatus status) {
        // 手动回滚事务
        transactionManager.rollback(status);
        if (status.isNewTransaction() && !status.isCompleted()) {
            transactionManager.commit(status);
        }
    }
}
